package com.xinqi.xinqidev.bigmodel.ws;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.xinqi.xinqidev.bigmodel.entity.JsonParse;
import com.xinqi.xinqidev.bigmodel.entity.RoleContent;
import com.xinqi.xinqidev.bigmodel.entity.Text;
import com.xinqi.xinqidev.bigmodel.gptutils.AuthUrlUtils;
import com.xinqi.xinqidev.bigmodel.gptutils.HistoryUtils;
import com.xinqi.xinqidev.bigmodel.gptutils.WebSocketUtils;
import com.xinqi.xinqidev.bigmodel.secret.PersonalGPTSecret;
import com.xinqi.xinqidev.pojo.entity.QuestionAnswerLog;
import com.xinqi.xinqidev.service.QuestionAnswerLogService;
import com.xinqi.xinqidev.utils.JsonUtils;
import com.xinqi.xinqidev.utils.MessageUtils;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;

import javax.websocket.Session;
import java.io.IOException;
import java.util.*;

import static com.xinqi.xinqidev.common.BigModelContents.HOST_URL;

/**
 * https://www.xfyun.cn/doc/spark/Web.html
 * https://console.xfyun.cn/services/bm2
 * https://xinghuo.xfyun.cn/sparkapi?ch=jK5dWZO
 */
//todo
@Slf4j
@Getter
public class BigModelNew extends WebSocketListener {
    private Boolean wsCloseFlag;
    // 环境治理的重要性  环保  人口老龄化  我爱我的祖国
    private String NewQuestion = "";
    private Boolean totalFlag = true; // 控制提示用户是否输入
    public String totalAnswer = ""; // 大模型的答案汇总

    private Session session;//session对象，用来发送远程的请求
    public List<RoleContent> historyList; // 对话历史存储集合

    private Long userId;

    private QuestionAnswerLogService questionAnswerLogService;

    // 构造函数
    public BigModelNew(String NewQuestion,
                       Session session,
                       List<RoleContent> historyList,
                       QuestionAnswerLogService questionAnswerLogService,
                       Long userId) {
        this.NewQuestion = NewQuestion;
        this.session = session;
        this.historyList = historyList;
        this.questionAnswerLogService = questionAnswerLogService;
        this.userId = userId;
    }

    @Override
    public void onOpen(WebSocket webSocket, Response response) {
        super.onOpen(webSocket, response);
        System.out.print("大模型：");
        sendMessage(webSocket);
    }

    private void sendMessage(WebSocket webSocket) {
        try {
            JSONObject requestJson = new JSONObject();

            JSONObject header = new JSONObject();  // header参数-header部分
            header.put("app_id", PersonalGPTSecret.getAppId());
            header.put("uid", UUID.randomUUID().toString().substring(0, 10));

            JSONObject parameter = new JSONObject(); // parameter参数-parameter.chat部分
            JSONObject chat = new JSONObject();
            chat.put("domain", "generalv2");
            chat.put("temperature", 0.5);
            chat.put("max_tokens", 2048);//https://www.xfyun.cn/doc/spark/Web.html#_1-%E6%8E%A5%E5%8F%A3%E8%AF%B4%E6%98%8E
//                chat.put("max_tokens", 4096);
            parameter.put("chat", chat);

            JSONObject payload = new JSONObject(); // payload参数
            JSONObject message = new JSONObject();
            JSONArray text = new JSONArray();

            // 历史问题获取
            if (historyList.size() > 0) {
                for (RoleContent tempRoleContent : historyList) {
                    text.add(JSON.toJSON(tempRoleContent));
                }
            }
            // 最新问题
            RoleContent roleContent = new RoleContent();
            roleContent.setRole("user");
            roleContent.setContent(NewQuestion);
            text.add(JSON.toJSON(roleContent));
            historyList.add(roleContent);
            message.put("text", text);
            payload.put("message", message);

            requestJson.put("header", header);
            requestJson.put("parameter", parameter);
            requestJson.put("payload", payload);
            // System.err.println(requestJson); // 可以打印看每次的传参明细
            webSocket.send(requestJson.toString());

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * text 是1.4 接口响应里的payload.
     *
     * @param webSocket
     * @param text
     */
    @Override
    public void onMessage(WebSocket webSocket, String text) {
        // System.out.println(userId + "用来区分那个用户的结果" + text);
        JsonParse myJsonParse = JsonUtils.gson.fromJson(text, JsonParse.class);
        if (myJsonParse.getHeader().getCode() != 0) {
            System.out.println("发生错误，错误码为：" + myJsonParse.getHeader().getCode());
            String sId = myJsonParse.getHeader().getSid();
            System.out.println("本次请求的sid为：" + sId);
            log.debug("webSocket3 is " + webSocket);



//            questionAnswerLogService.save(//这里无法使用mybatis
//                    new QuestionAnswerLog()
//                    .setUserId(userId)
//                    .setQuestion(NewQuestion)
//                    .setAnswer(totalAnswer)
//                    .setIsExistException(true)
//                    .setSId(sId)
//            );

            //help gc
            WebSocketUtils.closeAndGC(webSocket);
            questionAnswerLogService = null;
            session = null;
            totalFlag = true;

        }

        List<Text> textList = myJsonParse.getPayload().getChoices().getText();
        for (Text temp : textList) {
//            System.out.println("answer is "+ temp.getContent());//这个应该是是输出
            System.out.print(temp.getContent());//这里是输出 todo temp.getContent() 需要插入数据库，字段：
            // todo answer for 用户id(bigint) ,createTime(初始时创建即可) , answer(估计是一个blog),question(估计也是blog)
            //todo 万一 answer缺失怎么办？会不会出现这个情况？那么数据库的表的字段需要增加什么字段吗？
            // 思考：缺失就缺失吧，只能再加一个字段来标志是否缺失，所以说这个字段应该是tinyInt 要么0要么1，默认为1代表问题回答完毕。反之表示回答存在异常。
            totalAnswer += temp.getContent();
        }

        if (myJsonParse.getHeader().getStatus() == 2) {

            String response = MessageUtils.getMessage(totalAnswer);//todo 返回的内容
            log.debug("要保存到数据库的数据是：" + response);
            log.debug("要保存到数据库的totalAnswer是：" + totalAnswer);
            QuestionAnswerLog questionAnswerLog = new QuestionAnswerLog()
                    .setUserId(userId)
                    .setQuestion(NewQuestion)
                    .setAnswer(totalAnswer);

            log.debug("questionAnswerLog is " + questionAnswerLog);
//            boolean isSuccess = questionAnswerLogService.save(
//                    questionAnswerLog
//            );//这里保存不了，为什么？应该是没有给予spring来管理
//            log.debug("isSuccess is " + isSuccess);


            log.debug("数据即将发送。。。。。。。");
            //totalAnswer为最后的结果。
            try {
                session.getBasicRemote().sendText(response);//发送给客户端。
            } catch (IOException e) {
                e.printStackTrace();
                log.debug("数据: " + response + " \n 发送失败。。。。");
            }

            // 可以关闭连接，释放资源

            RoleContent roleContent = new RoleContent("assistant", totalAnswer);

            //如果size不足以12000，那么可以继续走：
            if (!HistoryUtils.canAddHistory(historyList)) historyList.remove(0);

            historyList.add(roleContent);//这里是添加gpt的回复。

            //这里是等到执行完毕，就关掉这个webSocket
            WebSocketUtils.closeAndGC(webSocket);
            questionAnswerLogService = null;
            session = null;

            //这个是最后一个，什么是最后一个？就是gpt回复你的问题可能分多次返回，于是因此多次返回前，输入的问题是不会被被gpt回答的。
            // 这里可以做一个接收中途的问题到一个队列里面，当之前的回答都打完了就从该队列里获取问题继续相应回答。
            //不过这里也可以不去做一个队列，直接就是阻塞。通过totalFlag做阻塞。
            totalFlag = true;
        }

    }

    @Override
    public void onFailure(WebSocket webSocket, Throwable t, Response response) {
        super.onFailure(webSocket, t, response);
        try {
            if (null != response) {
                int code = response.code();
                System.out.println("onFailure code:" + code);
                System.out.println("onFailure body:" + response.body().string());
                if (101 != code) {
                    System.out.println("connection failed");
                    System.exit(0);
                }
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}